package com.fzu.geometa.validate.test;

import com.fzu.geometa.validate.function.EmptyFunction;
import com.fzu.geometa.validate.util.XmlValidator;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.XPath;
import org.jaxen.FunctionContext;
import org.jaxen.XPathFunctionContext;
import org.testng.Assert;
import org.testng.Reporter;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.StringWriter;

public class ISO19123Validation {
    // 注册自定义 Xpath 函数
    static {
        FunctionContext instance = XPathFunctionContext.getInstance();
        XPathFunctionContext fc = (XPathFunctionContext) instance;
        fc.registerFunction(null, "empty", new EmptyFunction());
    }

    // 是否通过验证
    private boolean validated;

    // 测试文档
    private Document testDoc;

    // 非空测试
    private void notEmptyTest(String xpathExpr) {
        Reporter.log("XPath expression: " + xpathExpr);
        if (!xpathExpr.startsWith("empty")) {
            throw new RuntimeException("The outermost function must be 'empty'.");
        }
        // 计算结果
        Object res = xpathEvaluate(xpathExpr);
        reportEvaluateResults("false", res.toString());
        // 实际结果与预期结果对比
        Assert.assertEquals(res, false);
    }

    // 报告计算结果
    private void reportEvaluateResults(String expected, String actual) {
        Reporter.log("Expected: " + expected);
        Reporter.log("Actual: " + actual);
    }

    // xpath 计算
    private Object xpathEvaluate(String xpathExpr) {
        if (testDoc == null) {
            throw new RuntimeException("Test document not found");
        }
        XPath xpath = testDoc.createXPath(xpathExpr);
        Object res = xpath.evaluate(testDoc);
        return res;
    }

    // 报告验证结果
    private void xsdValidationTest() {

        if (validated) {
            Reporter.log("XML Schema validation passed");
        } else {
            Reporter.log("XML Schema validation failed");
            throw new RuntimeException("XML Schema validation failed");
        }
    }

    @BeforeTest(groups = {DataType.rectified, DataType.referenceable},
            description = "Parses url and xml document")
    @Parameters({"test-data"})
    public void init(String testData) throws IOException, DocumentException, SAXException {
        Reporter.log("Input data: \n" + testData);
        // 解析文档
        try {
            testDoc = DocumentHelper.parseText(testData);
        } catch (DocumentException e) {
            e.printStackTrace();
            Reporter.log("Failed to parse document");
            throw e;
        }

        // 创建验证器
        String schemaRefs = "/conf/schema-refs-19123.xml";
        StringWriter strWriter = new StringWriter();
        XmlValidator validator = null;
        try {
            validator = new XmlValidator(schemaRefs, strWriter);
        } catch (IOException | SAXException e) {
            e.printStackTrace();
            Reporter.log("Failed to create validator");
            throw e;
        }

        // 文档验证
        try {
            validated = validator.validateByXmlStr(testData);
        } catch (Exception e) {
            Reporter.log("RuntimeException" + e.getMessage());
            throw new RuntimeException(e);
        } finally {
            Reporter.log(strWriter.toString());
        }
    }

    @Test(groups = {DataType.rectified, DataType.referenceable}, priority = 1,
            description = "CoverageValidation")
    public void req1() {
        Reporter.log("[req1: CoverageValidation]");
        xsdValidationTest();
    }

    @Test(groups = {DataType.rectified}, priority = 2,
            description = "RectifiedGridCoverage")
    public void req2() {
        Reporter.log("[req2: RectifiedGridCoverage]");
        String xpathExpr = "empty(//*[local-name()='RectifiedGridCoverage'])";
        notEmptyTest(xpathExpr);
    }

    @Test(groups = {DataType.referenceable}, priority = 3,
            description = "ReferenceableGridCoverage")
    public void req3() {
        Reporter.log("[req3: ReferenceableGridCoverage]");
        String xpathExpr = "empty(//*[local-name()='ReferenceableGridCoverage'])";
        notEmptyTest(xpathExpr);
    }

}
